use std::{ fs::File, path:: Path, sync:: Mutex};
use confsync::pb::{agent_server::{Agent,AgentServer},SyncRequest,SyncResponse,manage_client::ManageClient,ReportRequest};
use tokio::task;
use std::sync::OnceLock;
use std::process::Command;
use inotify::{Inotify, WatchMask};
use std::os::unix::fs::PermissionsExt;
static SERVER_ADDR: OnceLock<Mutex<String>> = OnceLock::new();
static PATH: OnceLock<Mutex<String>> = OnceLock::new();
#[derive(Debug, Default)]
pub struct AgentService{
}

#[tonic::async_trait]
impl Agent for AgentService {
    async fn sync(&self, request: tonic::Request<SyncRequest>) -> Result<tonic::Response<SyncResponse>, tonic::Status> {
        let server_addr = SERVER_ADDR.get_or_init(|| Mutex::new(String::new()));
        let mut addr = server_addr.lock().unwrap();
        let  path=PATH.get_or_init(|| Mutex::new(String::new()));
        let remote_addr = request.remote_addr().unwrap();
        let inner_request = request.into_inner();
        if addr.is_empty() {
            *addr = remote_addr.to_string();
            *path.lock().unwrap() = inner_request.path;
        }
        std::fs::create_dir_all(path.lock().unwrap().as_str())?;
        for file in inner_request.files {
            let path = Path::new(path.lock().unwrap().as_str()).join(file.path);
            let content = file.content;
            if let Some(parent) = path.parent() {
                std::fs::create_dir_all(parent)?;
            }
            std::fs::write(path, content)?;
        }
        let apply_path = Path::new(path.lock().unwrap().as_str()).join("apply.sh");
        let apply_script = File::open(&apply_path)?;
        let mut permissions = apply_script.metadata()?.permissions();
        permissions.set_mode(0o755);
        apply_script.set_permissions(permissions)?;
        Command::new(apply_path).output()?;
        

        task::spawn(bootstrap_file_watcher(path.lock().unwrap().clone(),addr.clone()));
        let response = SyncResponse {
            success: true,
        };
        Ok(tonic::Response::new(response))
    }
}

#[tokio::main]
async fn main()  {
   bootstrap_server().await;
}

async fn bootstrap_server() {
    let addr = "[::1]:50051".parse().unwrap();
    let agent = AgentService::default();
    println!("AgentServer listening on {}", addr);
    tonic::transport::Server::builder()
        .add_service(AgentServer::new(agent))
        .serve(addr)
        .await
        .unwrap();
}



async fn bootstrap_file_watcher(path: String,addr :String) {
    let mut inotify = Inotify::init().unwrap();
    let watch_path = Path::new(&path);
    let watch_mask = WatchMask::CREATE | WatchMask::DELETE | WatchMask::MODIFY;
    let wd = inotify.add_watch(watch_path, watch_mask).unwrap();
    loop {
        let mut buffer = [0u8; 4096];
        let events = inotify.read_events_blocking(&mut buffer).unwrap();
        for event in events {
            if event.wd == wd {
                let mut client = ManageClient::connect(format!("http://{}", addr)).await.unwrap();
                let request = tonic::Request::new(ReportRequest {
                });
                let response = client.report(request).await.unwrap();
                println!("{:?}", response);
                return ;
            }
        }
    }
}
   